package com.star.uc.biz;

import com.google.common.collect.Maps;
import com.star.common.core.context.ThreadContext;
import com.star.common.core.contract.constant.ContextConstants;
import com.star.common.core.contract.enums.StatusEnum;
import com.star.common.core.utils.BizAssert;
import com.star.common.kit.language.chain.LanguageChainFilterFactory;
import com.star.common.protocol.contract.dto.HeaderDTO;
import com.star.message.api.contract.command.SendSmsCommand;
import com.star.message.api.service.MessageSmsApi;
import com.star.uc.contract.dto.request.MonitorRequest;
import com.star.uc.contract.dto.request.SmsSendRequest;
import com.star.uc.contract.enums.*;
import com.star.uc.contract.enums.monitor.SmsCaptchaMonitorEnum;
import com.star.uc.contract.exception.LimitRateException;
import com.star.uc.contract.param.UserListConfigParam;
import com.star.uc.infras.repository.model.UserListConfigBO;
import com.star.uc.infras.utils.GenerateCodeUtils;
import com.star.uc.infras.utils.LimitFilterUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 短信业务处理类
 *
 * @author zhouhengzhe
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SmsBizService {

    private final LanguageChainFilterFactory languageChainFilterFactory;

    private final LimitFilterUtils limitFilterUtils;

    private final GenerateCodeUtils generateCodeUtils;

    private final MessageSmsApi messageSmsApi;


    /**
     * 发送短信
     *
     * @param smsSendRequest 短信发送请求
     */
    public void sendSms(SmsSendRequest smsSendRequest) {
        //语言类型过滤
        String languageCode = languageChainFilterFactory.handle(HeaderDTO.getHeaderContext().getLanguageCode());
        //查询虚拟手机号，高费用短信列表
        List<UserListConfigBO> userListConfigBOList = UserListConfigBO.repository().list(UserListConfigParam
                .builder()
                .status(StatusEnum.OPEN.getCode())
                .tenantId(smsSendRequest.getTenantId())
                .build());
        //如果是中文，就需要判断是否是虚拟手机号=>国际区号等于86，且手机号码前缀不为空，且手机号码前缀在虚拟手机号码前缀列表中
        BizAssert.isFalseWarn(ContextConstants.CHINA_COUNTRY_CODE.equals(smsSendRequest.getCountryCode())
                        && (CollectionUtils.isNotEmpty(userListConfigBOList)
                        && (userListConfigBOList.stream()
                        .filter(userListConfigBO -> ListTypeEnum.VIRTUAL_PHONE_PREFIX.getCode().equals(userListConfigBO.getListType()))
                        .map(UserListConfigBO::getValue)
                        .collect(Collectors.toSet())
                        .contains(smsSendRequest.getPhone().substring(0, 3)))),
                UserErrorEnum.VIRTUAL_PHONE_PREFIX);
        //是否是高费用短信
        BizAssert.isFalseWarn(CollectionUtils.isNotEmpty(userListConfigBOList)
                        && userListConfigBOList.stream()
                        .filter(userListConfigBO -> ListTypeEnum.HIGH_COST.getCode().equals(userListConfigBO.getListType()))
                        .map(UserListConfigBO::getValue)
                        .collect(Collectors.toSet()).contains(smsSendRequest.getCountryCode()),
                UserErrorEnum.HIGH_COST_AREA);
        //ip,邮件分时天，ip分时天限流，风控
        limitIpAndEmailCount(smsSendRequest, languageCode);

        //生成验证码
        String code = generateCodeUtils.generateCode(UserGeneratorCodeTypeEnum.PHONE.getCode(),
                smsSendRequest.getSendType(), smsSendRequest.getTenantId(), smsSendRequest.getCountryCode() + smsSendRequest.getPhone());
        Map<String, String> codeMap = Maps.newHashMap();
        codeMap.put("code", code);
        //发送短信
        messageSmsApi.sendSms(SendSmsCommand
                .builder()
                .tenantId(smsSendRequest.getTenantId())
                .languageCode(languageCode)
                .countryCode(smsSendRequest.getCountryCode())
                .phone(smsSendRequest.getPhone())
                .codeMap(codeMap)
                .build());
        //监控，默认不处理，记录发送成功事件，
        SmsCaptchaMonitorEnum smsCaptchaMonitorEnum = SmsCaptchaMonitorEnum.getByCode();
        smsCaptchaMonitorEnum.monitor(MonitorRequest
                .builder()
                .tenantId(smsSendRequest.getTenantId())
                .sendType(smsSendRequest.getSendType())
                .languageCode(languageCode)
                .value(smsSendRequest.getCountryCode() + smsSendRequest.getPhone())
                .flag(Boolean.TRUE)
                .reason("commit")
                .build());
        //发送成功后的语言续费
        smsCaptchaMonitorEnum.cost(MonitorRequest
                .builder()
                .tenantId(smsSendRequest.getTenantId())
                .sendType(smsSendRequest.getSendType())
                .languageCode(languageCode)
                .value(smsSendRequest.getCountryCode() + smsSendRequest.getPhone())
                .flag(Boolean.TRUE)
                .reason("high-expense")
                .cost(BigDecimal.ONE)
                .build());
    }

    /**
     * ip,邮件分时天，ip分时天限流，风控
     *
     * @param smsSendRequest 短信发送请求
     * @param languageCode   语言类型
     */
    private void limitIpAndEmailCount(SmsSendRequest smsSendRequest, String languageCode) {
        try {
            //ip限流（同一个应用同一个IP只能注册10个，防止人家用ip刷账号，虽然他有代理，但是我们就是要恶心他）
            limitFilterUtils.limitIp(smsSendRequest.getCountryCode() + smsSendRequest.getPhone(),
                    smsSendRequest.getTenantId(), smsSendRequest.getClientType(), LimitTypeEnum.PHONE.getCode());

            //同一个应用同一个邮箱限流(分，时，天)，简单理解为就是一个邮箱在某段时间内只能发送几个邮件
            limitFilterUtils.limitSendEmailOrSmsOrIpCount(smsSendRequest.getCountryCode() + smsSendRequest.getPhone(),
                    smsSendRequest.getTenantId(), LimitTypeEnum.PHONE.getCode(), smsSendRequest.getClientType());

            //同一个应用同一个IP限流(分，时，天)，简单理解为就是一个IP在某段时间内只能发送几个短信，（跟邮箱共用）
            limitFilterUtils.limitSendEmailOrSmsOrIpCount(ThreadContext.get(ContextConstants.IP),
                    smsSendRequest.getTenantId(), LimitTypeEnum.IP.getCode(), smsSendRequest.getClientType());
        } catch (LimitRateException e) {
            //监控，默认不处理
            SmsCaptchaMonitorEnum smsCaptchaMonitorEnum = SmsCaptchaMonitorEnum.getByCode();
            smsCaptchaMonitorEnum.monitor(MonitorRequest
                    .builder()
                    .tenantId(smsSendRequest.getTenantId())
                    .sendType(smsSendRequest.getSendType())
                    .languageCode(languageCode)
                    .value(smsSendRequest.getCountryCode() + smsSendRequest.getPhone())
                    .flag(Boolean.FALSE)
                    .reason("rate-limit")
                    .build());
            throw e;
        }
    }

}
